/**
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain an copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.littlejie.circleprogress;

import com.littlejie.circleprogress.utils.*;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;

/**
 * 水波进度条
 *
 * @author fuchi
 * @since 2021-04-02
 */

public class WaveProgress extends Component implements Component.DrawTask, Component.EstimateSizeListener,
        Component.ComponentStateChangedListener {

    private float waveShiftRatio = 0.0f; // 波浪横向偏移量
    private AnimatorValue animatorSetWave; // 波浪偏移动画

    private int mDefaultSize;
    private Point mCenterPoint; // 圆心
    private float mRadius; // 半径
    private RectFloat mRectF; // 圆的外接矩形
    private boolean antiAlias; // 是否开启抗锯齿
    private float mMaxValue; // 最大值
    private float mValue; // 当前值
    private float mPercent; // 当前进度
    private String mPrecisionFormat; // 中间显示的数字

    private Paint mHintPaint; // 绘制提示
    // private CharSequence mHint;
    private Color mHintColor;
    private int mHintSize;

    private Paint mPercentPaint;
    private int mValueSize;
    private Color mValueColor;

    private float mCircleWidth; // 圆环宽度
    private Paint mCirclePaint; // 圆环
    private Color mCircleColor; // 圆环颜色
    private Color mBgCircleColor; // 背景圆环颜色


    private Paint mWavePaint; // 深色水波
    private Color mDarkWaveColor; // 深色水波颜色
    private Color mLightWaveColor; // 浅色水波颜色

    private AnimatorValue mProgressAnimator;

    public WaveProgress(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttrSet attrs) {
        mDefaultSize = (int) ViewUtils.getInstance().vp2Px(context, Constant.DEFAULT_SIZE);
        mRectF = new RectFloat();
        mCenterPoint = new Point();
        initAttrs(context, attrs);
        initPaint();
        mPrecisionFormat = MiscUtil.getPrecisionFormat(0);
        setValue(mValue); // 开始动画
        initWaveAnimation();
    }

    private void initAttrs(Context context, AttrSet attrSet) {
        antiAlias = ViewUtils.getInstance().getAttrBoolValue(attrSet, Constant.ROUNDPROGRESS_ANTIALIAS, Constant.ANTI_ALIAS);
        mMaxValue = ViewUtils.getInstance().getFloatValue(attrSet, Constant.ROUNDPROGRESS_MAXVALUE, Constant.DEFAULT_MAX_VALUE);
        mValue = ViewUtils.getInstance().getFloatValue(attrSet, Constant.ROUNDPROGRESS_VALUE, Constant.DEFAULT_VALUE);
        mValueSize = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.ROUNDPROGRESS_VALUESIZE, Constant.DEFAULT_VALUE_SIZE);
        mValueColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_VALUECOLOR, Color.BLACK);
        // mHint = ViewUtils.getInstance().getAttrStringValue(attrSet, Constant.ROUNDPROGRESS_HINT, "");
        mHintColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_HINTCOLOR, Color.BLACK);
        mHintSize = ViewUtils.getInstance().getAttrIntValue(attrSet, Constant.ROUNDPROGRESS_HINTSIZE, Constant.DEFAULT_HINT_SIZE);
        mCircleWidth = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.WAVEPROGRESS_CIRCLEWIDTH, Constant.DEFAULT_ARC_WIDTH);
        mCircleColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.WAVEPROGRESS_CIRCLECOLOR, Color.GREEN);
        mBgCircleColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.WAVEPROGRESS_BGCIRCLECOLOR, Color.WHITE);
        mDarkWaveColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.WAVEPROGRESS_DARKWAVECOLOR, new Color(ResourceTable.Color_darkWave));
        mLightWaveColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.WAVEPROGRESS_LIGHTWAVECOLOR, new Color(ResourceTable.Color_lightWave));

    }

    private void initPaint() {
        mHintPaint = new Paint();
        // 设置抗锯齿,会消耗较大资源，绘制图形速度会变慢。
        mHintPaint.setAntiAlias(antiAlias);
        // 设置绘制文字大小
        mHintPaint.setTextSize(mHintSize);
        // 设置画笔颜色
        mHintPaint.setColor(mHintColor);
        // 从中间向两边绘制，不需要再次计算文字
        mHintPaint.setTextAlign(TextAlignment.CENTER);

        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(antiAlias);
        mCirclePaint.setStrokeWidth(mCircleWidth);
        mCirclePaint.setStyle(Paint.Style.STROKE_STYLE);
        mCirclePaint.setStrokeCap(Paint.StrokeCap.BUTT_CAP);

        mWavePaint = new Paint();
        mWavePaint.setAntiAlias(antiAlias);
        mWavePaint.setStyle(Paint.Style.FILL_STYLE);

        mPercentPaint = new Paint();
        mPercentPaint.setTextAlign(TextAlignment.CENTER);
        mPercentPaint.setFakeBoldText(true);
        mPercentPaint.setAntiAlias(antiAlias);
        mPercentPaint.setColor(mValueColor);
        mPercentPaint.setTextSize(mValueSize);
    }

    /**
     * 同onMeasure方法
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     * @return boolean
     */
    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        setEstimatedSize(MiscUtil.measure(widthMeasureSpec, mDefaultSize),
                MiscUtil.measure(heightMeasureSpec, mDefaultSize));
        return false;
    }

    public void initPoint(Component component) {
        int minSize = Math.min(component.getWidth() - component.getPaddingLeft() - component.getPaddingRight() - 2 * (int) mCircleWidth,
                component.getHeight() - component.getPaddingTop() - component.getPaddingBottom() - 2 * (int) mCircleWidth);
        mRadius = (float) minSize / 2;
        mCenterPoint = new Point((float) component.getWidth() / 2, (float) component.getHeight() / 2);
        float halfWidth = mCircleWidth / 2;
        //绘制圆弧的边界
        mRectF.left = DecimalUtils.subMore(mCenterPoint.getPointX(), mRadius, halfWidth);
        mRectF.top = DecimalUtils.subMore(mCenterPoint.getPointY(), mRadius, halfWidth);
        mRectF.right = DecimalUtils.addMore(mCenterPoint.getPointX(), mRadius, halfWidth);
        mRectF.bottom = DecimalUtils.addMore(mCenterPoint.getPointY(), mRadius, halfWidth);
        System.out.println("onMeasure: 控件大小 = " + "(" + component.getWidth() + ", " + component.getHeight() + ")"
                + ";圆心坐标 = " + mCenterPoint.toString()
                + ";圆半径 = " + mRadius
                + ";圆的外接矩形 = " + mRectF.toString());
    }

    /**
     * 此方法加载onDraw方法以重新绘制组件
     */
    @Override
    public void invalidate() {
        super.invalidate();
        addDrawTask(this::onDraw);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        initPoint(component);
        drawCircle(canvas);
        updateWaveShader(canvas);
        drawProgress(canvas);
        exPercent = mPercent;
    }

    private float defaultWaterLevel;

    private void updateWaveShader(Canvas canvas) {
        float wavePercent = DecimalUtils.sub(1f, mPercent);
        float canvasSize = mRadius * 2;
        double defaultAngularFrequency = 2.0f * Math.PI / 1.0f / canvasSize;
        float defaultAmplitude = canvasSize * 0.06f;
        defaultWaterLevel = canvasSize * wavePercent;

        mWavePaint.setColor(mDarkWaveColor);
        for (int beginX = 0; beginX < canvasSize; beginX++) {
            double wx = beginX * defaultAngularFrequency;
            float x = beginX + canvasSize * waveShiftRatio > canvasSize ? beginX + canvasSize * waveShiftRatio - canvasSize : beginX + canvasSize * waveShiftRatio;
            float sqrt = (float) Math.sqrt(mRadius * mRadius - Math.abs(x - mRadius) * Math.abs(x - mRadius));
            float yt = mRadius - sqrt;
            float yb = sqrt + mRadius;
            float beginY = Math.max((float) (defaultWaterLevel + defaultAmplitude * Math.sin(wx)), yt);

            float xPoint = x + 2 * (mCenterPoint.getPointX() - x);
            if (yb > beginY) {
                canvas.drawLine(new Point(xPoint - 15, beginY + 15), new Point(xPoint - 15, yb + 15), mWavePaint);
            }
        }

        mWavePaint.setColor(mLightWaveColor);
        float offsetwaveShiftRatio = waveShiftRatio - 0.25f;
        if (offsetwaveShiftRatio < 0) {
            offsetwaveShiftRatio += 1;
        }
        for (int beginX = 0; beginX < canvasSize; beginX++) {
            double wx = beginX * defaultAngularFrequency;
            float x = beginX + canvasSize * offsetwaveShiftRatio > canvasSize ? beginX + canvasSize * offsetwaveShiftRatio - canvasSize : beginX + canvasSize * offsetwaveShiftRatio;
            float sqrt = (float) Math.sqrt(mRadius * mRadius - Math.abs(x - mRadius) * Math.abs(x - mRadius));
            float yt = mRadius - sqrt;
            float yb = sqrt + mRadius;
            float beginY = Math.max((float) (defaultWaterLevel + defaultAmplitude * Math.sin(wx)), yt);
            if (yb > beginY) {
                canvas.drawLine(new Point(x + 15, beginY + 15), new Point(x + 15, yb + 15), mWavePaint);
                System.out.println("-------------此时y轴坐标 = " + (yb + 18) + ",beginY = " + beginY);

            }
        }
    }

    /**
     * 绘制圆环
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        canvas.save();
        canvas.rotate(270, mCenterPoint.getPointX(), mCenterPoint.getPointY());
        int currentAngle = (int) (360 * mPercent);
        //画背景圆环
        mCirclePaint.setColor(mBgCircleColor);
        Arc bgArc = new Arc();
        bgArc.setArc(currentAngle, 360 - currentAngle, false);
        canvas.drawArc(mRectF, bgArc, mCirclePaint);
        //画圆环
        mCirclePaint.setColor(mCircleColor);
        Arc arc = new Arc();
        arc.setArc(0, currentAngle, false);
        canvas.drawArc(mRectF, arc, mCirclePaint);
        canvas.restore();
    }


    private void drawProgress(Canvas canvas) {
        float mValueOffset = mCenterPoint.getPointY() + getBaselineOffsetFromY(mPercentPaint);
        canvas.drawText(mPercentPaint, String.format(mPrecisionFormat, mValue) + "%", mCenterPoint.getPointX(), mValueOffset);
    }

    private float getBaselineOffsetFromY(Paint paint) {
        return (float) MiscUtil.measureTextHeight(paint) / 2;
    }

    public float getMaxValue() {
        return mMaxValue;
    }

    public void setMaxValue(float maxValue) {
        mMaxValue = maxValue;
    }

    public float getValue() {
        return mValue;
    }

    /**
     * 设置当前值
     *
     * @param value
     */
    public void setValue(float value) {
        if (value > mMaxValue) {
            value = mMaxValue;
        }
        float start = mPercent;
        float end = value / mMaxValue;
        mValue = value;
        startAnimator(start, end);
    }

    private float exPercent = 0;
    private AnimatorValueUpdateUtil animatorValueUpdateUtil = new AnimatorValueUpdateUtil();


    private void startAnimator(final float start, float end) {
        //当start=0且end=0时，不需要启动动画
        if (start == 0 && end == 0) {
            return;
        }
        mProgressAnimator = new AnimatorValue();
        animatorValueUpdateUtil.ofFloat(exPercent, end);
        mProgressAnimator.setDuration(1000);
        mProgressAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                mPercent = (float) animatorValueUpdateUtil.getValue(v);
            }
        });
        mProgressAnimator.start();
    }


    @Override
    public void onComponentStateChanged(Component component, int i) {

    }

    private void initWaveAnimation() {
        // horizontal animation.
        animatorSetWave = new AnimatorValue();
        animatorSetWave.setLoopedCount(AnimatorValue.INFINITE);
        animatorSetWave.setDuration(1000);
        animatorSetWave.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                waveShiftRatio = v;
                invalidate();
            }
        });
        animatorSetWave.start();

    }

}
